Integrating FCKeditor
Tom M. Yeh,Chief Architect, Potix Corporation
December 27, 2005
- Revised on February 8, 2006
Applicable to ZK 2.1.1 and later
The Purpose
This article described the process to integrate FCKeditor as a ZK component. It is aimed to illustrate the component development, rather FCKeditor itself.
What is FCKeditor?
FCKeditor is a popular HTML on-line text edit developed by Frederico Caldeira Knabben.
What is ZK?
ZK is an event-driven, XHTML/XUL-based, AJAX-embedded, all Java framework to enable rich user interfaces for Web applications.
The Integration Goal
Goal: to have a ZK component that encapsulates a FCKeditor.
The component is called fckeditor. Its use is similar to textbox.
Demo
http://www.zkoss.org/zkdemo/test/fckeditor.zul
<fckeditor id="ed" value="Some" onChange="tb.value = self.value"/>
<textbox id="tb" rows="6" onChange="ed.value = self.value"/>
The source codes could be downloaded from sourceforge. It is recommended to download one of zk-FCKeditor*.zip and unzip it first, before reading the rest of this article.
Notice that the onChange event works only on Internet Explorer, because it is based on the onBlur event which FCKeditor 2.2 doesn't support other browser yet.
The Process
Copy Files from the FCKeditor Distribution
- Basically we have to copy
fckconfig.js, fckeditor.js, fckstyles.xml, fcktemplates.xml
, and all files, except _source
, under theeditor
directory. Refer to FCKeditor documents for details.
- As mentioned in ZK Developer's Reference: Packing Code, we could embed web resources in JAR file. Simply put, we have to put them under the web directory. To avoid being confused with other projects, we put them under the
/web/js/ext/FCKeditor
directory locatable by the Java classpath.
- In other words, files located under this directory are all copied from the FCKeditor distribution without modification.
- For the
fckeditor
component, we need to prepare only four files:lang-addon.xml, fckez.js, fckeditor.dsp, and FCKeditor.java.
lang-addon.xml
- First, we have to prepare a file called lang-addon.xml. It must be put under the /metainfo/zk directory locatable by the Java classpath.
- It describes components that will be added to an existent language as depicted below.
<language-addon>
<addon-name>fckez</addon-name>
<language-name>xul/html</language-name>
<zscript>
import org.zkforge.fckez.*;
</zscript>
<component>
<component-name>fckeditor</component-name>
<component-class>org.zkforge.fckez.FCKeditor</component-class>
<mold>
<mold-name>default</mold-name>
<mold-uri>~./fckez/fckeditor.dsp</mold-uri>
</mold>
</component>
</language-addon>
<addon-name>
- The name of this language addon.
<language-name>
- Specifies the language to which the components shall be added.
- A language is defined by a file called lang.xml. Its format is similar but it assumes the specified language doesn't exist yet.
<zscript>
- Specifies any Java codes that need to execute (via BeanShell) before interpreting a page.
<component>
- Defines a component by specifying the component name and the Java class to use. You could define as many as component as you want.
- In addition to specify a new component, you could override any existent one here. For example, you might prefer your own class instead of
org.zkoss.zul.Window.
- In addition to specify a new component, you could override any existent one here. For example, you might prefer your own class instead of
<component><mold>
- A component might have zero, one or multiple molds. If it doesn't have any mold, it has to take care of rendering by overriding the
redraw
method. If it does, the mold specified here will be used to render the component into HTML tags. The mold calleddefault
is the default mold.
fckeditor.dsp
- This file is a template to generate the corresponding HTML tags for each
fckeditor
component.
<%@ taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c" %>
<c:set var="self" value="${requestScope.arg.self}"/>
<c:set var="edid" value="${self.uuid}!ed"/>
<c:set var="eduri" value="~./js/ext/FCKeditor/editor/fckeditor.html?InstanceName=${edid}"/>
<c:set var="eduri" value="${eduri}&Toolbar=${self.toolbarSet}" unless="${empty self.toolbarSet}"/>
<div id="${self.uuid}"${self.htmlAttributes} zk_type="fckez.fckez.FCKeditor"
zk_src="${c:encodeURL(eduri)}">
<input type="hidden" id="${edid}" value="${c:escapeXML(self.value)}"/>
<input type="hidden" id="${edid}___Config" value="${self.configString}"/>
<iframe id="${edid}___Frame" src="${c:encodeURL('~./img/spacer.gif')}"
${c:attr('width',self.width)}${c:attr('height',self.height)} frameborder="no" scrolling="no"></iframe>
</div>
- The skeleton is based on the document of FCKeditor. You need to declare two input field to hold the initial value and configuration, and an inline frame to hold the HTML editor.
- There are several things worth to mention, when we generate HTML tags for components.
- DSP is similar to JSP. Refer to ZK Developer's Reference: DSP for details. However, you could use JSP or other technologies, if you would like to.
- You must group the generated HTML tags under one HTML tag, because ZK Update Engine assumes it. If your component consists of multiple parts, you could use SPAN or DIV to group them.
- The topmost HTML tag must have the id attribute and the value must be the component's UUID.
- When ZK renders a page (DSP or not), a map is stored in the request's attribute called arg. The map currently has only item called self. It is the component being rederered.
- The zk_type attribute of the topmost HTML tag is optional. If specified, the Client Engine will initialize it by loading the module (a JavaScript file) and then invoke the init method, if any.
- In this case, we specify fckez.fckez.FCKeditor. It means the JavaScript codes are located at the file called /web/js/fckez/fckez.js, and this file must be loaded when the FCKeditor component is encountered. It also means the method called zkFCKeditor.init must be called, when any instance the FCKeditor component is created.
- Furthmore, the zkFCKeditor.setAttr method and others will be called when the server has updated an attribute according to application's codes. It will be described more detailedly later.
- We don't specify the src attribute of iframe to the correct URL. Rather, we initialize it by copying from the zk_src attribute of div, when zkFCKeditor.init is called.
- Why?
- To have better performance, ZK won't load JavaScript files until the corresponding components are encountered. On the other hand, to have better responsiveness, HTML is generated and render first. Thus, we to ensure our JavaScript codes for FCKeditor are loaded, when FCKeditor starts to initialize itself.
- For most components, such as DOJO, it is not necessary to applying this trick. Rather, you usually use the zk.load method to load additional JavaScript files.
FCKeditor.java
- It is the Java class to represent the fckeditor component. First, we have to extend it from org.zkoss.zk.ui.AbstractComponent. Second, we implement the org.zkoss.zk.ui.event.Inputable interface for the input-type components, such as fckeditor. Then, you implement whatever behavior you want.
- If a method (usually a setter, aka., mutator) is going to change the visual representation of a component, you could do one of the following.
- 1. Invoke the smartUpdate method to notify the client the value of an attribute is changed. Once called, the client will receive the setAttr or rmAttr command, depending on whether the value is null. ZK Client Engine will handle it automatically.
- For fckeditor, smartUpdate is all we need.
- However, the default behavior of setAttr and rmAttr is not. Thus, we have to provide some JavaScript codes to handle.
public void setValue(String value) {
if (value == null) value = "";
if (!value.equals(_value)) {
_value = value;
smartUpdate("value", value);
}
}
- 2. Invoke invalidate(INNER) or invalidate(OUTER) to redraw the whole component.
- 3. Invoke the response method to send a specific command. Notice that smartUpdate is similar to response, except all smart updates will be removed once invalidate is called against the same component.
fckez.js
- It is common that we have to provide some JavaScript codes to handle the behavior of a component at the client. In this example, the JavaScript file is called fckez.js. Its content is straightforward.
function zkFCKeditor() {}
zkFCKeditor.init = function (cmp) {
FCKeditorAPI = __FCKeditorNS = null;
var ifr = document.getElementById(cmp.id + "!ed___Frame");
ifr.src = cmp.getAttribute("zk_src");
};
zkFCKeditor.setAttr = function (ed, name, value) {
var ifr = document.getElementById(ed.id + "!ed___Frame");
var fed = FCKeditorAPI.GetInstance(ed.id + "!ed");
if (ifr) {
switch (name) {
case "width":
ifr.width = value;
return true;
case "height":
ifr.height = value;
return true;
case "value":
if (fed) fed.SetHTML(value);
return true;
}
}
return false;
};
zkFCKeditor.init
- As mentioned, it is called when a component is initialized. For FCKeditor, we simple initialize the src attribute of iframe to the correct URL.
- However, FCKeditor 2.2 has a bug that it doesn't clean up some variables when unloaded. Thus, We have to clean up them here.
zkFCeditor.setAttr
- As mentioned above, the fckeditor component uses smartUpdate to notify the client what attribute to modify. However, the attributes, in fckeditor, are not embedded as part of the topmost HTML tag, so we cannot let ZK Client Engine to handle it. Rather, we have to provide a JavaScript file to handle it.
Behind the Scene
- When ZK Client Engine receives the setAttr and rmAttr command, it checks whether the zk_type attribute is defined. For fckeditor, the zk_type attribute defined with FCKeditor. ZK Client Engine then invokes the zkFCKeditor.setAttr function, if any.
- Thus, you could implement zkFCKeditor.setAttr to intercept the setAttr command. This method could return true to notify ZK Client Engine that the attribute has been updated, or false to notify that ZK Client Engine shall handle it.
- You could explode more attribute to the fckeditor component by intercepting more attributes here. In the current release, we process only width, height and value, as illustrated above.
Summary
ZK has been deliberately designed to simplify not only the application development, but also the component development. As shown in this example, the development process of a component is as simple as four steps.
- Defines the name, class and mold (aka., template) in lang-addon.xml.
- Provides a mold for generating corresponding HTML tags for a component.
- Provides a Java class to represent the behavior of a component.
- Provides a optional JavaScript code to handle specific commands sent by the Java class.
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |